Render this report with
~/spinal_cord_paper/scripts/Gg_devel_scWGCNA_module_analysis_render.sh.
library(Seurat)
Attaching SeuratObject
library(WGCNA)
Loading required package: dynamicTreeCut
Loading required package: fastcluster
Attaching package: ‘fastcluster’
The following object is masked from ‘package:stats’:
hclust
Attaching package: ‘WGCNA’
The following object is masked from ‘package:stats’:
cor
library(tidyr)
library(ggplot2)
library(stringr)
library(patchwork)
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ tibble 3.1.8 ✔ dplyr 1.0.10
✔ readr 1.4.0 ✔ forcats 0.5.1
✔ purrr 0.3.4
── Conflicts ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
library(cowplot)
Attaching package: ‘cowplot’
The following object is masked from ‘package:patchwork’:
align_plots
library(pheatmap)
library(gridExtra)
Attaching package: ‘gridExtra’
The following object is masked from ‘package:dplyr’:
combine
source("~/Neuraltube/scripts/heatmap4.R")
Load individual seurat and test WGCNA data
The individual data sets are the Day 5 (Gg_D05_ctrl_seurat_070323),
Day 7 (Gg_D07_ctrl_seurat_070323), and Day 10 (Gg_ctrl_1_seurat_070323)
chicken spinal cord sets. The test WGCNA data are the modules calculated
on the integrated data set of all three stages.
se_path <- c("Gg_D05_ctrl_seurat_070323",
"Gg_D07_ctrl_seurat_070323",
"Gg_ctrl_1_seurat_070323")
clust_col <- read.csv("~/spinal_cord_paper/annotations/broad_cluster_marker_colors.csv") %>%
rename(broad = broad_cluster) %>%
select(-marker)
Order of the broad clusters for plotting purposes.
broad_order <- c("progenitors",
"FP",
"RP",
"FP/RP",
"neurons",
"OPC",
"MFOL",
"pericytes",
"microglia",
"blood",
"vasculature"
)
my.ses <- list()
col_table <- list()
ord_levels <- list()
for (i in seq(se_path)) {
# load the data sets
my.se <- readRDS(paste0("~/spinal_cord_paper/data/", se_path[i], ".rds"))
annot <- read.csv(list.files("~/spinal_cord_paper/annotations",
pattern = str_remove(se_path[i], "_seurat_\\d{6}"),
full.names = TRUE))
if(length(table(annot$number)) != length(table(my.se$seurat_clusters))) {
stop("Number of clusters must be identical!")
}
# rename for left join
annot <- annot %>%
mutate(fine = paste(fine, number, sep = "_")) %>%
mutate(number = factor(number, levels = 1:nrow(annot))) %>%
rename(seurat_clusters = number)
# cluster order for vln plots
ord_levels[[i]] <- annot$fine[order(match(annot$broad, broad_order))]
# create index for color coding
col_table[[i]] <- annot %>%
left_join(clust_col, by = "broad") %>%
select(c("fine", "color"))
# add cluster annotation to meta data
my.se@meta.data <- my.se@meta.data %>%
rownames_to_column("rowname") %>%
left_join(annot, by = "seurat_clusters") %>%
mutate(fine = factor(fine, levels = annot$fine)) %>%
column_to_rownames("rowname")
my.ses[[i]] <- my.se
}
names(my.ses) <- c("D05", "D07", "D10")
names(col_table) <- c("D05", "D07", "D10")
names(ord_levels) <- c("D05", "D07", "D10")
rm(my.se, annot)
# The reference WGCNA data. We can have several, if we want to test many at the same time
WGCNA_data = list()
WGCNA_data[[1]] = readRDS("~/spinal_cord_paper/output/Gg_devel_int_scWGCNA_250723.rds")
my.wsub =list()
my.wsub[[1]]= c(1:22)
# the name of each sample, as they appear in my.files and in the metadata of the combined object
my.samplenames = c("D05", "D07", "D10")
# This is just to add a little bit more sense to the modules, so that we don't get just a color. Corresponds to WGCNA_data
my.modulenames = list()
my.modulenames[[1]] = c(1:22)
Module gene correlation
Here, we do a correlation matrix / heatmap, to see which cell
clusters group togheter. This helps us to make more detailed
dotplots.
This part of the script can still be used to compare several WGCNA
datasets in parallel.
# broad cluster color table
all_col <- do.call(rbind, col_table) %>%
rownames_to_column("sample") %>%
mutate(sample = substr(sample, 1, 3)) %>%
mutate(sample_celltype = paste(sample, fine, sep = "_")) %>%
select(c("color", "sample_celltype", "sample"))
Average expression data
cell_table <- my.metam %>%
rownames_to_column("cell_ID") %>%
select("sample_celltype", "cell_ID")
avg.mod.eigengenes <- WGCNA_data[[1]]$sc.MEList$averageExpr %>%
rownames_to_column("cell_ID") %>%
left_join(cell_table, by = "cell_ID") %>%
column_to_rownames("cell_ID")
# add metadata
avg.mod.eigengenes.mean <- avg.mod.eigengenes %>%
group_by(sample_celltype) %>%
summarise_all("mean") %>%
column_to_rownames("sample_celltype")
spearman correlation heatmap
annotations
#The colors for the samples and clusters. First the closest to the heatmap. Add a white space to easy the eye and make less confussing
my.heatcols <- as.matrix(data.frame(
sample= as.character(my.colsm[match(all_col$sample, names(my.colsm))]),
"." = "white",
cluster= as.character(all_col$color[match(rownames(avg.mod.eigengenes.mean), all_col$sample_celltype)]))
)
# names and colors for the heatmap annotation
annot_name <- data.frame(
"Celltypes" = all_col$sample_celltype,
"Sample" = all_col$sample,
row.names = all_col$sample_celltype)
annot_module <- data.frame(
"Module" = colnames(avg.mod.eigengenes)[1:22],
row.names = colnames(avg.mod.eigengenes)[1:22]
)
pheat_col_table <- do.call(rbind, col_table) %>%
rownames_to_column("sample") %>%
mutate(sample = substr(sample, 1,3)) %>%
mutate(fine = paste(sample, fine, sep = "_"))
# match color table with annotation
pheat_col_table <- pheat_col_table[match(annot_name$Celltypes, pheat_col_table$fine),]
annot_col <- list(
Celltypes = pheat_col_table$color,
Sample = c(D05 = "#A4A4A4",
D07 = "#515151",
D10 = "#000000"),
Module = str_remove(colnames(avg.mod.eigengenes)[1:22], "AE")
)
names(annot_col[[1]]) <- annot_name$Celltypes
names(annot_col[[3]]) <- colnames(avg.mod.eigengenes)[1:22]
heat_col <- colorRampPalette(colors = c("dodgerblue4","dodgerblue", "white", "red", "darkred"))
#Calculate the distance 1-cor, and then calculate dendograms to cluster the clusters
my.hclust = hclust(as.dist(1-cor(t(avg.mod.eigengenes.mean), method = "spearman")))
# lower limit to scale color bar
low_limit <- 100 - abs(round(range(cor(t(avg.mod.eigengenes.mean), method = "spearman"))[1]*100))
pheatmap(cor(t(avg.mod.eigengenes.mean), method = "spearman"),revC = T,
main = "spearman correlation of average module eigengene expression by cluster\n Dendrogram based on 'distance 1-cor'",
fontsize = 8,
color = heat_col(200)[low_limit:200],
show_colnames = F,
cluster_rows = my.hclust,
cluster_cols = my.hclust,
treeheight_row = 0,
annotation_col = annot_name,
annotation_colors = annot_col,
annotation_legend = F,
border_color = NA
)
colbar <- c(min(cor(t(avg.mod.eigengenes.mean), method = "spearman")),
max(cor(t(avg.mod.eigengenes.mean), method = "spearman")))
#Calculate the distance 1-cor, and then calculate dendograms to cluster the clusters
my.hclust = as.dendrogram(hclust(as.dist(1-cor(t(avg.mod.eigengenes.mean), method = "spearman"))))
#Plot!
pdf("~/spinal_cord_paper/figures/Fig_1_module_gene_heatmap_spearman.pdf", width = 11, height = 11)
heatmap.4(cor(t(avg.mod.eigengenes.mean), method = "spearman"),
col = colorRampPalette(c("dodgerblue4","dodgerblue", "white", "red", "darkred"))(n = 1000),
trace="none",
lhei=c(0.2,1.6),
lwid=c(0.2,1.6),
scale = "none",margins = c(10,10),
cexCol = 0.75,
revC = TRUE,
ColSideColors = my.heatcols,
RowSideColors = t(my.heatcols[,3:1]),Rowv = my.hclust, Colv = my.hclust)
pdf("~/spinal_cord_paper/figures/Fig_1_module_gene_heatmap_color_key_spearman.pdf", width = 10, height = 10)
# heatmap since color key is missing in first heatmap
heatmap.4(rbind(colbar, colbar),
col = colorRampPalette(c("dodgerblue4","dodgerblue", "white", "red", "darkred"))(n = 1000),
trace="none",
lhei=c(0.2,1.6),
lwid=c(0.2,1.6),
scale = "none",margins = c(10,10),
cexCol = 0.75)

heatmap of module pseudobulk average expression
# module colors
my.colcols = as.matrix(names(table(WGCNA_data[[1]]$dynamicCols)))
htmp <- pheatmap(as.matrix(avg.mod.eigengenes.mean),
fontsize = 8,
scale = "column",
color = colorRampPalette(c("#4d2d87","#7c55a3", "white", "#ed9921", "#895d25"))(n = 1000),
annotation_row = annot_name,
annotation_col = annot_module,
annotation_colors = annot_col,
annotation_legend = F,
border_color = NA)
Error in grid.newpage() :
could not open file '/scicore/home/tschoppp/sacfab01/.local/share/rstudio/notebooks/7247CC19-Gg_devel_scWGCNA_module_analysis/1/6B3D10EC1f9f2767/coa2rns2f0mbr_t/_rs_chunk_plot_002.png'
Load the integrated data set
The integrated data set on which the WGCNA is calculated. We plot it,
split by the original cell types from the three original samples.
# This is a file of all the combined mouse datasets, normalized and such.
my.sec = readRDS("~/spinal_cord_paper/data/Gg_devel_int_seurat_250723.rds")
identical(rownames(my.metam), colnames(my.sec))
[1] TRUE
my.metam$sample_celltype <- factor(my.metam$sample_celltype, levels = all_col$sample_celltype)
#Set the identities of the integrated data, to the annotated clusters
my.sec = SetIdent(my.sec, value = my.metam$sample_celltype)
p1 <- DimPlot(
my.sec,
reduction = "tsne",
label = TRUE,
repel = TRUE,
cols = all_col$color,
split.by = "orig.ident"
) +
NoLegend()
p1
pdf("~/spinal_cord_paper/figures/Devel_split_tsne.pdf", height = 7, width = 15)
#Plot split tsne
p1

Avg. module exp. by stage
tSNE DimPlots showing the average expression of each module by
stage.

AE over time
We plot the average expression of each module in the three stages and
the 5 broad cell type clusters present in all 3 stages.
VlnPlots of avg. module exp. by stage and seurat cluster
colored by module
# reorder seurat clusters
for (i in seq(my.ses)) {
my.ses[[i]]$seurat_clusters <- factor(
my.ses[[i]]$seurat_clusters,
levels = levels(my.ses[[i]]$seurat_clusters)[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]
)
}
vplots <- list()
for (i in seq(my.ses)) {
mods <- colnames(my.ses[[i]][[]])[grep("^AE",colnames(my.ses[[i]][[]]))]
vplots[[i]] <- VlnPlot(
my.ses[[i]],
features = mods[module_order],
group.by = "seurat_clusters",
stack = TRUE, flip = TRUE,
cols = substring(mods, 3)[module_order]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
}
vplots[[1]]
vplots[[2]]
vplots[[3]]
pdf("~/spinal_cord_paper/figures/Fig_2_AE_by_cluster_modcol.pdf", height = 20, width = 10)
vplots[[1]]
vplots[[2]]
vplots[[3]]
colored by cell type
clust_col <- read.csv("~/spinal_cord_paper/annotations/broad_cluster_marker_colors.csv")
vplots_id <- list()
for (i in seq(my.ses)) {
mods <- colnames(my.ses[[i]][[]])[grep("^AE",colnames(my.ses[[i]][[]]))]
vplots_id[[i]] <- VlnPlot(
my.ses[[i]],
features = mods[module_order],
group.by = "seurat_clusters",
stack = TRUE, flip = TRUE,
fill.by = "ident",
cols = col_table[[i]]$color[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
}
vplots_id[[1]]
vplots_id[[2]]
vplots_id[[3]]
pdf("~/spinal_cord_paper/figures/Fig_2_AE_by_cluster_clucol.pdf", height = 20, width = 10)
vplots_id[[1]]
vplots_id[[2]]
vplots_id[[3]]
MN modules
For the figures we specifically select the two MN modules and plot
them as Vln plots.

VlnPlot(
my.sec,
features = mods[module_order],
group.by = "sample_celltype",
stack = TRUE, flip = TRUE,
cols = substring(mods, 3)[module_order]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
pdf("~/spinal_cord_paper/figures/Fig_2_AE_by_cluster_integrated_data.pdf", height = 20, width = 30)
VlnPlot(
my.sec,
features = mods[module_order],
group.by = "sample_celltype",
stack = TRUE, flip = TRUE,
cols = substring(mods, 3)[module_order]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
```r
# Date and time of Rendering
Sys.time()
sessionInfo()
```
LS0tCnRpdGxlOiAiRGV2ZWxfaW50IFdHQ05BIG1vZHVsZXMgZXhwcmVzc2lvbiBpbiBOVCBENSwgRDcsIGFuZCBEMTAgc2FtcGxlcyIKYXV0aG9yOiAiRmFiaW8gU2FjaGVyIgpkYXRlOiAiMDQuMDguMjAyMyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogIGh0bWxfbm90ZWJvb2s6CiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDgKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KClJlbmRlciB0aGlzIHJlcG9ydCB3aXRoIH4vc3BpbmFsX2NvcmRfcGFwZXIvc2NyaXB0cy9HZ19kZXZlbF9zY1dHQ05BX21vZHVsZV9hbmFseXNpc19yZW5kZXIuc2guCgpgYGB7ciBzZXR1cH0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoV0dDTkEpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShncmlkRXh0cmEpCnNvdXJjZSgifi9OZXVyYWx0dWJlL3NjcmlwdHMvaGVhdG1hcDQuUiIpCgpgYGAKCiMgTG9hZCBpbmRpdmlkdWFsIHNldXJhdCBhbmQgdGVzdCBXR0NOQSBkYXRhCgpUaGUgaW5kaXZpZHVhbCBkYXRhIHNldHMgYXJlIHRoZSBEYXkgNSAoR2dfRDA1X2N0cmxfc2V1cmF0XzA3MDMyMyksIERheSA3IChHZ19EMDdfY3RybF9zZXVyYXRfMDcwMzIzKSwgYW5kIERheSAxMCAoR2dfY3RybF8xX3NldXJhdF8wNzAzMjMpIGNoaWNrZW4gc3BpbmFsIGNvcmQgc2V0cy4gVGhlIHRlc3QgV0dDTkEgZGF0YSBhcmUgdGhlIG1vZHVsZXMgY2FsY3VsYXRlZCBvbiB0aGUgaW50ZWdyYXRlZCBkYXRhIHNldCBvZiBhbGwgdGhyZWUgc3RhZ2VzLgoKYGBge3IgZGF0YS1zZXRzfQpzZV9wYXRoIDwtIGMoIkdnX0QwNV9jdHJsX3NldXJhdF8wNzAzMjMiLAogICAgICAgICAgICAgIkdnX0QwN19jdHJsX3NldXJhdF8wNzAzMjMiLAogICAgICAgICAgICAgIkdnX2N0cmxfMV9zZXVyYXRfMDcwMzIzIikKCmNsdXN0X2NvbCA8LSByZWFkLmNzdigifi9zcGluYWxfY29yZF9wYXBlci9hbm5vdGF0aW9ucy9icm9hZF9jbHVzdGVyX21hcmtlcl9jb2xvcnMuY3N2IikgJT4lIAogIHJlbmFtZShicm9hZCA9IGJyb2FkX2NsdXN0ZXIpICU+JSAKICBzZWxlY3QoLW1hcmtlcikKCmBgYAoKT3JkZXIgb2YgdGhlIGJyb2FkIGNsdXN0ZXJzIGZvciBwbG90dGluZyBwdXJwb3Nlcy4KCmBgYHtyIG9yZGVyaW5nfQpicm9hZF9vcmRlciA8LSBjKCJwcm9nZW5pdG9ycyIsCiAgICAgICJGUCIsCiAgICAgICJSUCIsCiAgICAgICJGUC9SUCIsCiAgICAgICJuZXVyb25zIiwKICAgICAgIk9QQyIsCiAgICAgICJNRk9MIiwKICAgICAgInBlcmljeXRlcyIsCiAgICAgICJtaWNyb2dsaWEiLAogICAgICAiYmxvb2QiLAogICAgICAidmFzY3VsYXR1cmUiCiAgICAgICkKYGBgCgoKYGBge3Igc2V1cmF0LW9iamVjdHMtYW5kLWFubm90YXRpb25zfQpteS5zZXMgPC0gbGlzdCgpCmNvbF90YWJsZSA8LSBsaXN0KCkKb3JkX2xldmVscyA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEoc2VfcGF0aCkpIHsKICAjIGxvYWQgdGhlIGRhdGEgc2V0cwogIG15LnNlIDwtIHJlYWRSRFMocGFzdGUwKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2RhdGEvIiwgc2VfcGF0aFtpXSwgIi5yZHMiKSkKICBhbm5vdCA8LSByZWFkLmNzdihsaXN0LmZpbGVzKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSBzdHJfcmVtb3ZlKHNlX3BhdGhbaV0sICJfc2V1cmF0X1xcZHs2fSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFRSVUUpKQogIAogIGlmKGxlbmd0aCh0YWJsZShhbm5vdCRudW1iZXIpKSAhPSBsZW5ndGgodGFibGUobXkuc2Ukc2V1cmF0X2NsdXN0ZXJzKSkpIHsKICAgICBzdG9wKCJOdW1iZXIgb2YgY2x1c3RlcnMgbXVzdCBiZSBpZGVudGljYWwhIikKICB9CiAgCiAgIyByZW5hbWUgZm9yIGxlZnQgam9pbgogIGFubm90IDwtIGFubm90ICU+JSAKICAgIG11dGF0ZShmaW5lID0gcGFzdGUoZmluZSwgbnVtYmVyLCBzZXAgPSAiXyIpKSAlPiUgCiAgICBtdXRhdGUobnVtYmVyID0gZmFjdG9yKG51bWJlciwgbGV2ZWxzID0gMTpucm93KGFubm90KSkpICU+JSAKICAgIHJlbmFtZShzZXVyYXRfY2x1c3RlcnMgPSBudW1iZXIpIAogIAogICMgY2x1c3RlciBvcmRlciBmb3IgdmxuIHBsb3RzCiAgb3JkX2xldmVsc1tbaV1dIDwtIGFubm90JGZpbmVbb3JkZXIobWF0Y2goYW5ub3QkYnJvYWQsIGJyb2FkX29yZGVyKSldCiAgCiAgIyBjcmVhdGUgaW5kZXggZm9yIGNvbG9yIGNvZGluZwogIGNvbF90YWJsZVtbaV1dIDwtIGFubm90ICU+JQogICAgbGVmdF9qb2luKGNsdXN0X2NvbCwgYnkgPSAiYnJvYWQiKSAlPiUgCiAgICBzZWxlY3QoYygiZmluZSIsICJjb2xvciIpKQogIAogICMgYWRkIGNsdXN0ZXIgYW5ub3RhdGlvbiB0byBtZXRhIGRhdGEKICBteS5zZUBtZXRhLmRhdGEgPC0gbXkuc2VAbWV0YS5kYXRhICU+JSAKICAgIHJvd25hbWVzX3RvX2NvbHVtbigicm93bmFtZSIpICU+JSAKICAgIGxlZnRfam9pbihhbm5vdCwgYnkgPSAic2V1cmF0X2NsdXN0ZXJzIikgJT4lIAogICAgbXV0YXRlKGZpbmUgPSBmYWN0b3IoZmluZSwgbGV2ZWxzID0gYW5ub3QkZmluZSkpICU+JSAKICAgIGNvbHVtbl90b19yb3duYW1lcygicm93bmFtZSIpCiAgCiAgbXkuc2VzW1tpXV0gPC0gbXkuc2UKCn0KCm5hbWVzKG15LnNlcykgPC0gYygiRDA1IiwgIkQwNyIsICJEMTAiKQpuYW1lcyhjb2xfdGFibGUpIDwtIGMoIkQwNSIsICJEMDciLCAiRDEwIikKbmFtZXMob3JkX2xldmVscykgPC0gYygiRDA1IiwgIkQwNyIsICJEMTAiKQoKcm0obXkuc2UsIGFubm90KQpgYGAKCmBgYHtyIGlucHV0IGRhdGEsIGVjaG89VFJVRX0KIyBUaGUgcmVmZXJlbmNlIFdHQ05BIGRhdGEuIFdlIGNhbiBoYXZlIHNldmVyYWwsIGlmIHdlIHdhbnQgdG8gdGVzdCBtYW55IGF0IHRoZSBzYW1lIHRpbWUKV0dDTkFfZGF0YSA9IGxpc3QoKQpXR0NOQV9kYXRhW1sxXV0gPSByZWFkUkRTKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL291dHB1dC9HZ19kZXZlbF9pbnRfc2NXR0NOQV8yNTA3MjMucmRzIikKbXkud3N1YiA9bGlzdCgpCm15LndzdWJbWzFdXT0gYygxOjIyKQoKIyB0aGUgbmFtZSBvZiBlYWNoIHNhbXBsZSwgYXMgdGhleSBhcHBlYXIgaW4gbXkuZmlsZXMgYW5kIGluIHRoZSBtZXRhZGF0YSBvZiB0aGUgY29tYmluZWQgb2JqZWN0Cm15LnNhbXBsZW5hbWVzID0gYygiRDA1IiwgIkQwNyIsICJEMTAiKQoKIyBUaGlzIGlzIGp1c3QgdG8gYWRkIGEgbGl0dGxlIGJpdCBtb3JlIHNlbnNlIHRvIHRoZSBtb2R1bGVzLCBzbyB0aGF0IHdlIGRvbid0IGdldCBqdXN0IGEgY29sb3IuIENvcnJlc3BvbmRzIHRvIFdHQ05BX2RhdGEKbXkubW9kdWxlbmFtZXMgPSBsaXN0KCkKbXkubW9kdWxlbmFtZXNbWzFdXSA9IGMoMToyMikKCmBgYAoKIyBNb2R1bGUgZ2VuZSBjb3JyZWxhdGlvbgoKSGVyZSwgd2UgZG8gYSBjb3JyZWxhdGlvbiBtYXRyaXggLyBoZWF0bWFwLCB0byBzZWUgd2hpY2ggY2VsbCBjbHVzdGVycyBncm91cCB0b2doZXRlci4gVGhpcyBoZWxwcyB1cyB0byBtYWtlIG1vcmUgZGV0YWlsZWQgZG90cGxvdHMuICAKVGhpcyBwYXJ0IG9mIHRoZSBzY3JpcHQgY2FuIHN0aWxsIGJlIHVzZWQgdG8gY29tcGFyZSBzZXZlcmFsIFdHQ05BIGRhdGFzZXRzIGluIHBhcmFsbGVsLiAgCgpgYGB7cn0KIyBicm9hZCBjbHVzdGVyIGNvbG9yIHRhYmxlCmFsbF9jb2wgPC0gZG8uY2FsbChyYmluZCwgY29sX3RhYmxlKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKCJzYW1wbGUiKSAlPiUgCiAgbXV0YXRlKHNhbXBsZSA9IHN1YnN0cihzYW1wbGUsIDEsIDMpKSAlPiUgCiAgbXV0YXRlKHNhbXBsZV9jZWxsdHlwZSA9IHBhc3RlKHNhbXBsZSwgZmluZSwgc2VwID0gIl8iKSkgJT4lIAogIHNlbGVjdChjKCJjb2xvciIsICJzYW1wbGVfY2VsbHR5cGUiLCAic2FtcGxlIikpCmBgYAoKIyBNZXRhIGRhdGEKCmBgYHtyIG1ldGEtZGF0YX0KI0dldCBhIGRhdGFmcmFtZSB3aXRoIGFubm90YXRpb25zIGZvciBhbGwgdGhlIHNhbXBsZXMgYW5kIGNvbG9ycyB3ZSBuZWVkLgpteS5tZXRhbSA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogIG15Lm1ldGFtW1tpXV0gPC0gbXkuc2VzW1tpXV1bW11dCiAgcm93bmFtZXMobXkubWV0YW1bW2ldXSkgPC0gcGFzdGUwKHJvd25hbWVzKG15Lm1ldGFtW1tpXV0pLCAiXyIsIGkpCn0KbXkubWV0YW0gPC0gZG8uY2FsbChyYmluZCwgbXkubWV0YW0pCgpteS5tZXRhbSRvcmlnLmlkZW50IDwtIHN0cl9yZXBsYWNlX2FsbChteS5tZXRhbSRvcmlnLmlkZW50LCBwYXR0ZXJuID0gICJHZ19EMDVfY3RybCIsICJEMDUiKQpteS5tZXRhbSRvcmlnLmlkZW50IDwtIHN0cl9yZXBsYWNlX2FsbChteS5tZXRhbSRvcmlnLmlkZW50LCBwYXR0ZXJuID0gICJHZ19EMDdfY3RybCIsICJEMDciKQpteS5tZXRhbSRvcmlnLmlkZW50IDwtIHN0cl9yZXBsYWNlX2FsbChteS5tZXRhbSRvcmlnLmlkZW50LCBwYXR0ZXJuID0gICJHZ19jdHJsXzEiLCAiRDEwIikKCiMgbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUwKHN1YnN0cihteS5tZXRhbSRvcmlnLmlkZW50LDcsOSksIl8iLG15Lm1ldGFtJHNldXJhdF9jbHVzdGVycykKbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUwKG15Lm1ldGFtJG9yaWcuaWRlbnQsICJfIiwgbXkubWV0YW0kZmluZSkKCm15Lm1ldGFtIDwtIG15Lm1ldGFtICU+JSAKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiY2VsbF9JRCIpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oYWxsX2NvbCwgYnkgPSAic2FtcGxlX2NlbGx0eXBlIikgJT4lCiAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXModmFyID0gImNlbGxfSUQiKQoKCiMgZ2V0IHNhbXBsZSBjb2xvcnMKbXkuY29sc20gPSBjKCJncmV5IiwgImdyZXkzMCIsICJibGFjayIpCm5hbWVzKG15LmNvbHNtKSA8LSBjKCJEMDUiLCAiRDA3IiwgIkQxMCIpCiAKYGBgCgoKIyBBdmVyYWdlIGV4cHJlc3Npb24gZGF0YQoKYGBge3J9CgpjZWxsX3RhYmxlIDwtIG15Lm1ldGFtICU+JSAKICByb3duYW1lc190b19jb2x1bW4oImNlbGxfSUQiKSAlPiUgCiAgc2VsZWN0KCJzYW1wbGVfY2VsbHR5cGUiLCAiY2VsbF9JRCIpCgphdmcubW9kLmVpZ2VuZ2VuZXMgPC0gV0dDTkFfZGF0YVtbMV1dJHNjLk1FTGlzdCRhdmVyYWdlRXhwciAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKCJjZWxsX0lEIikgJT4lIAogIGxlZnRfam9pbihjZWxsX3RhYmxlLCBieSA9ICJjZWxsX0lEIikgJT4lIAogIGNvbHVtbl90b19yb3duYW1lcygiY2VsbF9JRCIpCiAgCgojIGFkZCBtZXRhZGF0YQoKYXZnLm1vZC5laWdlbmdlbmVzLm1lYW4gPC0gYXZnLm1vZC5laWdlbmdlbmVzICU+JQogIGdyb3VwX2J5KHNhbXBsZV9jZWxsdHlwZSkgJT4lCiAgc3VtbWFyaXNlX2FsbCgibWVhbiIpICU+JQogIGNvbHVtbl90b19yb3duYW1lcygic2FtcGxlX2NlbGx0eXBlIikKCmBgYAoKIyMgc3BlYXJtYW4gY29ycmVsYXRpb24gaGVhdG1hcAoKIyMjIGFubm90YXRpb25zCgpgYGB7ciBhbm5vdC1saXN0fQoKI1RoZSBjb2xvcnMgZm9yIHRoZSBzYW1wbGVzIGFuZCBjbHVzdGVycy4gRmlyc3QgdGhlIGNsb3Nlc3QgdG8gdGhlIGhlYXRtYXAuIEFkZCBhIHdoaXRlIHNwYWNlIHRvIGVhc3kgdGhlIGV5ZSBhbmQgbWFrZSBsZXNzIGNvbmZ1c3NpbmcKbXkuaGVhdGNvbHMgPC0gYXMubWF0cml4KGRhdGEuZnJhbWUoCiAgICBzYW1wbGU9IGFzLmNoYXJhY3RlcihteS5jb2xzbVttYXRjaChhbGxfY29sJHNhbXBsZSwgbmFtZXMobXkuY29sc20pKV0pLAogICAgIi4iID0gIndoaXRlIiwKICAgIGNsdXN0ZXI9IGFzLmNoYXJhY3RlcihhbGxfY29sJGNvbG9yW21hdGNoKHJvd25hbWVzKGF2Zy5tb2QuZWlnZW5nZW5lcy5tZWFuKSwgYWxsX2NvbCRzYW1wbGVfY2VsbHR5cGUpXSkpCiAgICApCgoKIyBuYW1lcyBhbmQgY29sb3JzIGZvciB0aGUgaGVhdG1hcCBhbm5vdGF0aW9uCmFubm90X25hbWUgPC0gZGF0YS5mcmFtZSgKICAiQ2VsbHR5cGVzIiA9IGFsbF9jb2wkc2FtcGxlX2NlbGx0eXBlLAogICJTYW1wbGUiICAgID0gYWxsX2NvbCRzYW1wbGUsCiAgcm93Lm5hbWVzID0gYWxsX2NvbCRzYW1wbGVfY2VsbHR5cGUpCiAgCgphbm5vdF9tb2R1bGUgPC0gZGF0YS5mcmFtZSgKICAiTW9kdWxlIiA9IGNvbG5hbWVzKGF2Zy5tb2QuZWlnZW5nZW5lcylbMToyMl0sCiAgcm93Lm5hbWVzID0gY29sbmFtZXMoYXZnLm1vZC5laWdlbmdlbmVzKVsxOjIyXQopCgpwaGVhdF9jb2xfdGFibGUgPC0gZG8uY2FsbChyYmluZCwgY29sX3RhYmxlKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKCJzYW1wbGUiKSAlPiUgCiAgbXV0YXRlKHNhbXBsZSA9IHN1YnN0cihzYW1wbGUsIDEsMykpICU+JSAKICBtdXRhdGUoZmluZSA9IHBhc3RlKHNhbXBsZSwgZmluZSwgc2VwID0gIl8iKSkKCiMgbWF0Y2ggY29sb3IgdGFibGUgd2l0aCBhbm5vdGF0aW9uCnBoZWF0X2NvbF90YWJsZSA8LSBwaGVhdF9jb2xfdGFibGVbbWF0Y2goYW5ub3RfbmFtZSRDZWxsdHlwZXMsIHBoZWF0X2NvbF90YWJsZSRmaW5lKSxdCgphbm5vdF9jb2wgPC0gbGlzdCgKICBDZWxsdHlwZXMgPSBwaGVhdF9jb2xfdGFibGUkY29sb3IsCiAgU2FtcGxlID0gYyhEMDUgPSAiI0E0QTRBNCIsCiAgICAgICAgICAgICBEMDcgPSAiIzUxNTE1MSIsCiAgICAgICAgICAgICBEMTAgPSAiIzAwMDAwMCIpLAogIE1vZHVsZSA9IHN0cl9yZW1vdmUoY29sbmFtZXMoYXZnLm1vZC5laWdlbmdlbmVzKVsxOjIyXSwgIkFFIikKICApCgpuYW1lcyhhbm5vdF9jb2xbWzFdXSkgPC0gYW5ub3RfbmFtZSRDZWxsdHlwZXMKbmFtZXMoYW5ub3RfY29sW1szXV0pIDwtIGNvbG5hbWVzKGF2Zy5tb2QuZWlnZW5nZW5lcylbMToyMl0KCmBgYAoKYGBge3IsICBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTN9CmhlYXRfY29sIDwtIGNvbG9yUmFtcFBhbGV0dGUoY29sb3JzID0gYygiZG9kZ2VyYmx1ZTQiLCJkb2RnZXJibHVlIiwgIndoaXRlIiwgInJlZCIsICJkYXJrcmVkIikpCgojQ2FsY3VsYXRlIHRoZSBkaXN0YW5jZSAxLWNvciwgYW5kIHRoZW4gY2FsY3VsYXRlIGRlbmRvZ3JhbXMgdG8gY2x1c3RlciB0aGUgY2x1c3RlcnMKbXkuaGNsdXN0ID0gaGNsdXN0KGFzLmRpc3QoMS1jb3IodChhdmcubW9kLmVpZ2VuZ2VuZXMubWVhbiksIG1ldGhvZCA9ICJzcGVhcm1hbiIpKSkKCiMgbG93ZXIgbGltaXQgdG8gc2NhbGUgY29sb3IgYmFyCmxvd19saW1pdCA8LSAxMDAgLSBhYnMocm91bmQocmFuZ2UoY29yKHQoYXZnLm1vZC5laWdlbmdlbmVzLm1lYW4pLCBtZXRob2QgPSAic3BlYXJtYW4iKSlbMV0qMTAwKSkKCnBoZWF0bWFwKGNvcih0KGF2Zy5tb2QuZWlnZW5nZW5lcy5tZWFuKSwgbWV0aG9kID0gInNwZWFybWFuIikscmV2QyA9IFQsCiAgICAgICAgICAgICAgICAgbWFpbiA9ICJzcGVhcm1hbiBjb3JyZWxhdGlvbiBvZiBhdmVyYWdlIG1vZHVsZSBlaWdlbmdlbmUgZXhwcmVzc2lvbiBieSBjbHVzdGVyXG4gRGVuZHJvZ3JhbSBiYXNlZCBvbiAnZGlzdGFuY2UgMS1jb3InIiwKICAgICAgICAgICAgICAgICBmb250c2l6ZSA9IDgsICAKICAgICAgICAgICAgICAgICBjb2xvciA9IGhlYXRfY29sKDIwMClbbG93X2xpbWl0OjIwMF0sCiAgICAgICAgICAgICAgICAgc2hvd19jb2xuYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gbXkuaGNsdXN0LAogICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29scyA9IG15LmhjbHVzdCwKICAgICAgICAgICAgICAgICB0cmVlaGVpZ2h0X3JvdyA9IDAsIAogICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gYW5ub3RfbmFtZSwKICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm90X2NvbCwKICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2xlZ2VuZCA9IEYsCiAgICAgICAgICAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEKICAgICAgICAgICAgICAgICApCgpjb2xiYXIgPC0gYyhtaW4oY29yKHQoYXZnLm1vZC5laWdlbmdlbmVzLm1lYW4pLCBtZXRob2QgPSAic3BlYXJtYW4iKSksCiAgICAgICAgICAgIG1heChjb3IodChhdmcubW9kLmVpZ2VuZ2VuZXMubWVhbiksIG1ldGhvZCA9ICJzcGVhcm1hbiIpKSkKCiNDYWxjdWxhdGUgdGhlIGRpc3RhbmNlIDEtY29yLCBhbmQgdGhlbiBjYWxjdWxhdGUgZGVuZG9ncmFtcyB0byBjbHVzdGVyIHRoZSBjbHVzdGVycwpteS5oY2x1c3QgPSBhcy5kZW5kcm9ncmFtKGhjbHVzdChhcy5kaXN0KDEtY29yKHQoYXZnLm1vZC5laWdlbmdlbmVzLm1lYW4pLCBtZXRob2QgPSAic3BlYXJtYW4iKSkpKQoKI1Bsb3QhCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX21vZHVsZV9nZW5lX2hlYXRtYXBfc3BlYXJtYW4ucGRmIiwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gMTEpCmhlYXRtYXAuNChjb3IodChhdmcubW9kLmVpZ2VuZ2VuZXMubWVhbiksIG1ldGhvZCA9ICJzcGVhcm1hbiIpLAogICAgICAgICAgY29sID0gY29sb3JSYW1wUGFsZXR0ZShjKCJkb2RnZXJibHVlNCIsImRvZGdlcmJsdWUiLCAid2hpdGUiLCAicmVkIiwgImRhcmtyZWQiKSkobiA9IDEwMDApLAogICAgICAgICAgdHJhY2U9Im5vbmUiLAogICAgICAgICAgbGhlaT1jKDAuMiwxLjYpLAogICAgICAgICAgbHdpZD1jKDAuMiwxLjYpLAogICAgICAgICAgc2NhbGUgPSAibm9uZSIsbWFyZ2lucyA9IGMoMTAsMTApLAogICAgICAgICAgY2V4Q29sID0gMC43NSwKICAgICAgICAgIHJldkMgPSBUUlVFLAogICAgICAgICAgQ29sU2lkZUNvbG9ycyA9IG15LmhlYXRjb2xzLAogICAgICAgICAgUm93U2lkZUNvbG9ycyA9IHQobXkuaGVhdGNvbHNbLDM6MV0pLFJvd3YgPSBteS5oY2x1c3QsIENvbHYgPSBteS5oY2x1c3QpCgoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfbW9kdWxlX2dlbmVfaGVhdG1hcF9jb2xvcl9rZXlfc3BlYXJtYW4ucGRmIiwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTApCiMgaGVhdG1hcCBzaW5jZSBjb2xvciBrZXkgaXMgbWlzc2luZyBpbiBmaXJzdCBoZWF0bWFwCmhlYXRtYXAuNChyYmluZChjb2xiYXIsIGNvbGJhciksCiAgICAgICAgICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoImRvZGdlcmJsdWU0IiwiZG9kZ2VyYmx1ZSIsICJ3aGl0ZSIsICJyZWQiLCAiZGFya3JlZCIpKShuID0gMTAwMCksCiAgICAgICAgICB0cmFjZT0ibm9uZSIsCiAgICAgICAgICBsaGVpPWMoMC4yLDEuNiksCiAgICAgICAgICBsd2lkPWMoMC4yLDEuNiksCiAgICAgICAgICBzY2FsZSA9ICJub25lIixtYXJnaW5zID0gYygxMCwxMCksCiAgICAgICAgICBjZXhDb2wgPSAwLjc1KQoKYGBgCgojIyBoZWF0bWFwIG9mIG1vZHVsZSBwc2V1ZG9idWxrIGF2ZXJhZ2UgZXhwcmVzc2lvbgoKYGBge3IsICBmaWcuaGVpZ2h0PTE1LCBmaWcud2lkdGg9MTB9CiMgbW9kdWxlIGNvbG9ycwpteS5jb2xjb2xzID0gYXMubWF0cml4KG5hbWVzKHRhYmxlKFdHQ05BX2RhdGFbWzFdXSRkeW5hbWljQ29scykpKQoKaHRtcCA8LSBwaGVhdG1hcChhcy5tYXRyaXgoYXZnLm1vZC5laWdlbmdlbmVzLm1lYW4pLAogICAgICAgICBmb250c2l6ZSA9IDgsCiAgICAgICAgIHNjYWxlID0gImNvbHVtbiIsCiAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCIjNGQyZDg3IiwiIzdjNTVhMyIsICJ3aGl0ZSIsICIjZWQ5OTIxIiwgIiM4OTVkMjUiKSkobiA9IDEwMDApLAogICAgICAgICBhbm5vdGF0aW9uX3JvdyA9IGFubm90X25hbWUsCiAgICAgICAgIGFubm90YXRpb25fY29sID0gYW5ub3RfbW9kdWxlLAogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm90X2NvbCwKICAgICAgICAgYW5ub3RhdGlvbl9sZWdlbmQgPSBGLAogICAgICAgICBib3JkZXJfY29sb3IgPSBOQSkKCm1vZHVsZV9vcmRlciA8LSBodG1wW1sidHJlZV9jb2wiXV0kb3JkZXIKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX2RldmVsX21vZHVsZV92X2NsdXN0ZXJzX2hlYXRtYXAucGRmIiwgd2lkdGggPSA3LCBoZWlnaHQgPSAxMCkKcGhlYXRtYXAoYXMubWF0cml4KGF2Zy5tb2QuZWlnZW5nZW5lcy5tZWFuKSwKICAgICAgICAgZm9udHNpemUgPSA4LAogICAgICAgICBzY2FsZSA9ICJjb2x1bW4iLAogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygiIzRkMmQ4NyIsIiM3YzU1YTMiLCAid2hpdGUiLCAiI2VkOTkyMSIsICIjODk1ZDI1IikpKG4gPSAxMDAwKSwKICAgICAgICAgYW5ub3RhdGlvbl9yb3cgPSBhbm5vdF9uYW1lLAogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGFubm90X21vZHVsZSwKICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vdF9jb2wsCiAgICAgICAgIGFubm90YXRpb25fbGVnZW5kID0gRiwKICAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEpCgoKYGBgCgoKCiMgTG9hZCB0aGUgaW50ZWdyYXRlZCBkYXRhIHNldAoKVGhlIGludGVncmF0ZWQgZGF0YSBzZXQgb24gd2hpY2ggdGhlIFdHQ05BIGlzIGNhbGN1bGF0ZWQuIFdlIHBsb3QgaXQsIHNwbGl0IGJ5IHRoZSBvcmlnaW5hbCBjZWxsIHR5cGVzIGZyb20gdGhlIHRocmVlIG9yaWdpbmFsIHNhbXBsZXMuIAoKYGBge3IgZGltcGxvdHMsIGZpZy53aWR0aD0xMH0KIyBUaGlzIGlzIGEgZmlsZSBvZiBhbGwgdGhlIGNvbWJpbmVkIG1vdXNlIGRhdGFzZXRzLCBub3JtYWxpemVkIGFuZCBzdWNoLgpteS5zZWMgPSByZWFkUkRTKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2RhdGEvR2dfZGV2ZWxfaW50X3NldXJhdF8yNTA3MjMucmRzIikKCmlkZW50aWNhbChyb3duYW1lcyhteS5tZXRhbSksIGNvbG5hbWVzKG15LnNlYykpCgpteS5tZXRhbSRzYW1wbGVfY2VsbHR5cGUgPC0gZmFjdG9yKG15Lm1ldGFtJHNhbXBsZV9jZWxsdHlwZSwgbGV2ZWxzID0gYWxsX2NvbCRzYW1wbGVfY2VsbHR5cGUpCiNTZXQgdGhlIGlkZW50aXRpZXMgb2YgdGhlIGludGVncmF0ZWQgZGF0YSwgdG8gdGhlIGFubm90YXRlZCBjbHVzdGVycwpteS5zZWMgPSBTZXRJZGVudChteS5zZWMsIHZhbHVlID0gbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlKQoKcDEgPC0gRGltUGxvdCgKICBteS5zZWMsCiAgcmVkdWN0aW9uID0gInRzbmUiLAogIGxhYmVsID0gVFJVRSwKICByZXBlbCA9IFRSVUUsCiAgY29scyA9IGFsbF9jb2wkY29sb3IsCiAgc3BsaXQuYnkgPSAib3JpZy5pZGVudCIKICApICsgCiAgTm9MZWdlbmQoKQoKcDEKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0RldmVsX3NwbGl0X3RzbmUucGRmIiwgaGVpZ2h0ID0gNywgd2lkdGggPSAxNSkKI1Bsb3Qgc3BsaXQgdHNuZQpwMQpgYGAKCiMgQXZnLiBtb2R1bGUgZXhwLiBieSBzdGFnZQoKdFNORSBEaW1QbG90cyBzaG93aW5nIHRoZSBhdmVyYWdlIGV4cHJlc3Npb24gb2YgZWFjaCBtb2R1bGUgYnkgc3RhZ2UuCgpgYGB7ciBBRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NDB9Cgpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICAjIHByZXBhcmUgYXZlcmFnZSBleHByZXNzaW9uIHRhYmxlCiAgdG1wIDwtIGF2Zy5tb2QuZWlnZW5nZW5lc1ssMToyMl0gJT4lCiAgICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiY2VsbF9JRCIpICU+JQogICAgZHBseXI6OmZpbHRlcihncmVwbChwYXN0ZTAoIl8iLCBpLCAiJCIpLCBjZWxsX0lEKSkgJT4lCiAgICBkcGx5cjo6bXV0YXRlKGNlbGxfSUQgPSBzdHJpbmdyOjpzdHJfcmVtb3ZlX2FsbChjZWxsX0lELCBwYXN0ZTAoIl8iLCBpKSkpICU+JQogICAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXMoImNlbGxfSUQiKQogIAogIGlkZW50aWNhbChyb3duYW1lcyh0bXApLCBjb2xuYW1lcyhteS5zZXNbW2ldXSkpCiAgIyBhZGQgbWV0YSBkYXRhIHRvIHRoZSBzZXVyYXQgb2JqZWN0cwogIG15LnNlc1tbaV1dIDwtIEFkZE1ldGFEYXRhKG15LnNlc1tbaV1dLCB0bXApCn0KCiNtYXggYW5kIG1pbiBleHByZXNzaW9uIHBlciBtb2R1bGUgKGNvbHVtbiBtYXgpCm1vZF9tYXggPC0gYXBwbHkoYXZnLm1vZC5laWdlbmdlbmVzWywxOjIyXSwgTUFSR0lOID0gMiwgRlVOID0gbWF4KVttb2R1bGVfb3JkZXJdCm1vZF9taW4gPC0gYXBwbHkoYXZnLm1vZC5laWdlbmdlbmVzWywxOjIyXSwgTUFSR0lOID0gMiwgRlVOID0gbWluKVttb2R1bGVfb3JkZXJdCgptb2RwbG90cyA8LSBsaXN0KCkKbW9kcGxvdHNbWzFdXSA8LSBsaXN0KCkKbW9kcGxvdHNbWzJdXSA8LSBsaXN0KCkKbW9kcGxvdHNbWzNdXSA8LSBsaXN0KCkKCm1vZHVsZXNfaW5fb3JkZXIgPC0gY29sbmFtZXModG1wKVttb2R1bGVfb3JkZXJdCgojIHBsb3QgdGhlIG1vZHVsZXMgc3BsaXQgdG8gdGhlIHN0YWdlcwpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICBmb3IgKGogaW4gc2VxKG5jb2wodG1wKSkpIHsKICAKICAgIG1vZHBsb3RzW1tpXV1bW2pdXSAgPC0gRmVhdHVyZVBsb3QoCiAgICAgIG15LnNlc1tbaV1dLCBvcmRlciA9IFRSVUUsCiAgICAgIGZlYXR1cmVzID0gbW9kdWxlc19pbl9vcmRlcltqXSwKICAgICAgcmVkdWN0aW9uID0gInRzbmUiCiAgICAgICkgKwogICAgICBnZ3RpdGxlKHN0cmluZ3I6OnN0cl9yZW1vdmUobW9kdWxlc19pbl9vcmRlcltqXSwiXkFFIikpICsKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93PSJpdm9yeTIiLCBoaWdoPXN1YnN0cmluZyhtb2R1bGVzX2luX29yZGVyW2pdLCAzKSwgI2NvbG9ycyBpbiB0aGUgc2NhbGUKICAgICAgICAgICAgICAgICBsaW1pdHM9Yyhtb2RfbWluW2pdLCBtb2RfbWF4W2pdKSkgI3NhbWUgbGltaXRzIGZvciBwbG90cwoKICAgIAogICAgfQp9CgpmdWxsX3Bsb3QgPC0gYyhtb2RwbG90c1tbMV1dLCBtb2RwbG90c1tbMl1dLCBtb2RwbG90c1tbM11dKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JvYnMgPSBmdWxsX3Bsb3QsIG5jb2wgPSAzLCBhcy50YWJsZSA9IEZBTFNFKQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzJfZGV2ZWxfbW9kdWxlc19BRV9wbG90cy5wZGYiLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3MCkKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JvYnMgPSBmdWxsX3Bsb3QsIG5jb2wgPSAzLCBhcy50YWJsZSA9IEZBTFNFKQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvU3VwcF9GaWdfMl9tb2R1bGVzX2RhcmtncmVlbl9BRV9wbG90cy5wZGYiLCB3aWR0aCA9IDEzLCBoZWlnaHQgPSA0KQooZnVsbF9wbG90W1s5XV0gKyBmdWxsX3Bsb3RbWzMxXV0gKyBmdWxsX3Bsb3RbWzUzXV0pICsgcGxvdF9sYXlvdXQobmNvbCA9IDMsIGd1aWRlcyA9ICJjb2xsZWN0IikKYGBgCgojIEFFIG92ZXIgdGltZQoKV2UgcGxvdCB0aGUgYXZlcmFnZSBleHByZXNzaW9uIG9mIGVhY2ggbW9kdWxlIGluIHRoZSB0aHJlZSBzdGFnZXMgYW5kIHRoZSA1IGJyb2FkIGNlbGwgdHlwZSBjbHVzdGVycyBwcmVzZW50IGluIGFsbCAzIHN0YWdlcy4KCmBgYHtyIEFFLW92ZXItdGltZSwgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTJ9CiMgbW9kdWxlIGFubm90YXRpb25zCm1vZF9hbm5vdCA8LSByZWFkLmNzdigifi9zcGluYWxfY29yZF9wYXBlci9hbm5vdGF0aW9ucy9HZ19kZXZlbF9pbnRfc2NXR0NOQV9tb2R1bGVfYW5ub3RhdGlvbi5jc3YiKSAlPiUKICBkcGx5cjo6bXV0YXRlKG1vZHVsZSA9IHN0cl9yZXBsYWNlX2FsbChtb2R1bGUsICJcXGR7MSwyfVxcXyIsICJBRSIpKQoKbWV0YSA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogIG1ldGFbW2ldXSA8LSBteS5zZXNbW2ldXUBtZXRhLmRhdGEgJT4lCiAgICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiY2VsbF9JRCIpICU+JQogICAgZHBseXI6Om11dGF0ZShjZWxsX0lEID0gcGFzdGUwKGNlbGxfSUQsICJfIiwgaSkpICU+JQogICAgZHBseXI6OnNlbGVjdChjKCJjZWxsX0lEIiwgImJyb2FkIikpCn0KCm1ldGEgPC0gZG8uY2FsbChyYmluZCwgbWV0YSkKCiMgbWVhbiBhdmVyYWdlIGV4cHJlc3Npb24gYnkgc3RhZ2UKbWVhbl9BRSA8LSBhdmcubW9kLmVpZ2VuZ2VuZXNbLDE6MjJdICU+JQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJjZWxsX0lEIikgJT4lCiAgZHBseXI6Om11dGF0ZShzdGFnZSA9IHN0cmluZ3I6OnN0cl9zdWIoY2VsbF9JRCwgLTEpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHN0YWdlID0gZmFjdG9yKHN0YWdlLCBsZXZlbHMgPSBjKDE6MyksIGxhYmVscyA9IGMoIkQwNSIsICJEMDciLCAiRDEwIikpKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKG1ldGEsIGJ5ID0gImNlbGxfSUQiKSAlPiUKICB0aWJibGU6OmNvbHVtbl90b19yb3duYW1lcygiY2VsbF9JRCIpICU+JQogIHRpZHlyOjp1bml0ZSgic3RhZ2VfY2wiLCBzdGFnZSwgYnJvYWQsIHNlcCA9ICJfIikgJT4lCiAgZHBseXI6Omdyb3VwX2J5KHN0YWdlX2NsKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlX2VhY2gobWVhbikgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICBnYXRoZXIoa2V5PSJtb2R1bGUiLCB2YWx1ZSA9ICJBRSIsIC1zdGFnZV9jbCkgJT4lCiAgZHBseXI6OmxlZnRfam9pbihtb2RfYW5ub3RbLCBjKDEsMyldLCBieSA9ICJtb2R1bGUiKSAlPiUKICB0aWR5cjo6c2VwYXJhdGUoInN0YWdlX2NsIiwgYygic3RhZ2UiLCAiYnJvYWQiKSwgc2VwID0gIl8iLCByZW1vdmUgPSBGQUxTRSkgCgpsYWJlbHNfZG90cGxvdCA8LSBzdHJpbmdyOjpzdHJfcmVtb3ZlKG1vZHVsZXNfaW5fb3JkZXIsICJeQUUiKQpuYW1lcyhsYWJlbHNfZG90cGxvdCkgPC0gbW9kdWxlc19pbl9vcmRlcgoKcGxvdF9jbHVzdGVycyA8LSBjKCJwcm9nZW5pdG9ycyIsICJuZXVyb25zIiwgIlJQIiwgIkZQIiwgInBlcmljeXRlcyIsICJPUEMiLCAiTUZPTCIsICJtaWNyb2dsaWEiLCAiYmxvb2QiKQoKbWVhbl9tb2QgPC0gZ2dwbG90KGRhdGEgPSBtZWFuX0FFLAogIGFlcygKICAgIHggPSBzdGFnZSwKICAgIHkgPSBBRSwKICAgIGNvbG9yID0gZmFjdG9yKGJyb2FkLCBsZXZlbHMgPSBwbG90X2NsdXN0ZXJzKSwKICAgIGdyb3VwID0gYnJvYWQsCiAgICBsYWJlbCA9IGFubm90YXRpb24KICAgICkKICApICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY2x1c3RfY29sJGNvbG9yW21hdGNoKHBsb3RfY2x1c3RlcnMsIGNsdXN0X2NvbCRicm9hZCldKSArCiAgdGhlbWVfYncoKSArCiAgIyBmYWNldCB3cmFwIHdpdGggcmVvcmRlcmVkIGZhY3RvcnMKICBmYWNldF93cmFwKHZhcnMoZmFjdG9yKG1vZHVsZSwgbGV2ZWxzID0gdW5pcXVlKG1lYW5fQUUkbW9kdWxlKVttb2R1bGVfb3JkZXJdKSksCiAgICAgICAgICAgICBzY2FsZXMgPSAiZnJlZV95IiwKICAgICAgICAgICAgIG5yb3cgPSA0LAogICAgICAgICAgICAgbmNvbCA9IDYpICsKICBsYWJzKGNvbG9yID0gImJyb2FkIikgKwogIGdndGl0bGUoIkF2ZXJhZ2UgbW9kdWxlIGV4cHJlc3Npb24gYnkgc3RhZ2UiKQoKcGxvdGx5OjpnZ3Bsb3RseShtZWFuX21vZCkKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18yX21lYW5fbW9kX0FFLnBkZiIsIGhlaWdodCA9IDcsIHdpZHRoID0gMTIpCiNQbG90IHNwbGl0IHRzbmUKbWVhbl9tb2QKYGBgCgojIFZsblBsb3RzIG9mIGF2Zy4gbW9kdWxlIGV4cC4gYnkgc3RhZ2UgYW5kIHNldXJhdCBjbHVzdGVyIAoKIyMgY29sb3JlZCBieSBtb2R1bGUKCmBgYHtyIGF2ZXJhZ2UtbW9kdWxlLWV4cHJlc3Npb24tcGVyLWNsdXN0ZXIsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0yMH0KCiMgcmVvcmRlciBzZXVyYXQgY2x1c3RlcnMKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgbXkuc2VzW1tpXV0kc2V1cmF0X2NsdXN0ZXJzIDwtIGZhY3RvcigKICBteS5zZXNbW2ldXSRzZXVyYXRfY2x1c3RlcnMsCiAgbGV2ZWxzID0gbGV2ZWxzKG15LnNlc1tbaV1dJHNldXJhdF9jbHVzdGVycylbYXMuaW50ZWdlcihzdHJfZXh0cmFjdChvcmRfbGV2ZWxzW1tpXV0sICJcXGR7MSwyfSQiKSldCiAgKQoKfQoKdnBsb3RzIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgCiAgbW9kcyA8LSBjb2xuYW1lcyhteS5zZXNbW2ldXVtbXV0pW2dyZXAoIl5BRSIsY29sbmFtZXMobXkuc2VzW1tpXV1bW11dKSldCiAgCiAgdnBsb3RzW1tpXV0gPC0gVmxuUGxvdCgKICAgICAgICBteS5zZXNbW2ldXSwKICAgICAgICBmZWF0dXJlcyA9IG1vZHNbbW9kdWxlX29yZGVyXSwKICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLAogICAgICAgIHN0YWNrID0gVFJVRSwgZmxpcCA9IFRSVUUsCiAgICAgICAgY29scyA9IHN1YnN0cmluZyhtb2RzLCAzKVttb2R1bGVfb3JkZXJdKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9ICJkYXNoZWQiKQogIAp9Cgp2cGxvdHNbWzFdXQp2cGxvdHNbWzJdXQp2cGxvdHNbWzNdXQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzJfQUVfYnlfY2x1c3Rlcl9tb2Rjb2wucGRmIiwgaGVpZ2h0ID0gMjAsIHdpZHRoID0gMTApCnZwbG90c1tbMV1dCnZwbG90c1tbMl1dCnZwbG90c1tbM11dCmBgYAoKIyMgY29sb3JlZCBieSBjZWxsIHR5cGUKCmBgYHtyIHZsbi1ieS1jbHVzdGVyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MjB9CmNsdXN0X2NvbCA8LSByZWFkLmNzdigifi9zcGluYWxfY29yZF9wYXBlci9hbm5vdGF0aW9ucy9icm9hZF9jbHVzdGVyX21hcmtlcl9jb2xvcnMuY3N2IikKCnZwbG90c19pZCA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogICAgCiAgbW9kcyA8LSBjb2xuYW1lcyhteS5zZXNbW2ldXVtbXV0pW2dyZXAoIl5BRSIsY29sbmFtZXMobXkuc2VzW1tpXV1bW11dKSldCiAgCiAgdnBsb3RzX2lkW1tpXV0gPC0gVmxuUGxvdCgKICAgIG15LnNlc1tbaV1dLAogICAgZmVhdHVyZXMgPSBtb2RzW21vZHVsZV9vcmRlcl0sCiAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLAogICAgc3RhY2sgPSBUUlVFLCBmbGlwID0gVFJVRSwKICAgIGZpbGwuYnkgPSAiaWRlbnQiLAogICAgY29scyA9IGNvbF90YWJsZVtbaV1dJGNvbG9yW2FzLmludGVnZXIoc3RyX2V4dHJhY3Qob3JkX2xldmVsc1tbaV1dLCAiXFxkezEsMn0kIikpXSkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsdHkgPSAiZGFzaGVkIikKfQoKdnBsb3RzX2lkW1sxXV0KdnBsb3RzX2lkW1syXV0KdnBsb3RzX2lkW1szXV0KCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18yX0FFX2J5X2NsdXN0ZXJfY2x1Y29sLnBkZiIsIGhlaWdodCA9IDIwLCB3aWR0aCA9IDEwKQp2cGxvdHNfaWRbWzFdXQp2cGxvdHNfaWRbWzJdXQp2cGxvdHNfaWRbWzNdXQpgYGAKCiMgTU4gbW9kdWxlcwoKRm9yIHRoZSBmaWd1cmVzIHdlIHNwZWNpZmljYWxseSBzZWxlY3QgdGhlIHR3byBNTiBtb2R1bGVzIGFuZCBwbG90IHRoZW0gYXMgVmxuIHBsb3RzLgoKYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD01fQp0bXAgPC0gYXZnLm1vZC5laWdlbmdlbmVzIAogIAppZGVudGljYWwocm93bmFtZXModG1wKSwgY29sbmFtZXMobXkuc2VjKSkKIyBhZGQgbWV0YSBkYXRhIHRvIHRoZSBpbnQgc2V1cmF0IG9iamVjdApteS5zZWMgPC0gQWRkTWV0YURhdGEobXkuc2VjLCB0bXApCm15LnNlYyA8LSBBZGRNZXRhRGF0YShteS5zZWMsIG15Lm1ldGFtW2MoImZpbmUiLCAic2FtcGxlX2NlbGx0eXBlIildKQoKY3VzdG9tX29yZGVyIDwtIGMocGFzdGUobmFtZXMob3JkX2xldmVscylbMV0sIG9yZF9sZXZlbHNbWzFdXSwgc2VwID0gJ18nKSwKICAgICAgICAgICAgICAgICAgcGFzdGUobmFtZXMob3JkX2xldmVscylbMl0sIG9yZF9sZXZlbHNbWzJdXSwgc2VwID0gJ18nKSwKICAgICAgICAgICAgICAgICAgcGFzdGUobmFtZXMob3JkX2xldmVscylbM10sIG9yZF9sZXZlbHNbWzNdXSwgc2VwID0gJ18nKSkKCm15LnNlYyRzYW1wbGVfY2VsbHR5cGUgPC0gZmFjdG9yKAogIG15LnNlYyRzYW1wbGVfY2VsbHR5cGUsCiAgbGV2ZWxzID0gY3VzdG9tX29yZGVyCiAgKQoKdmxuX2luZCA8LSBWbG5QbG90KG15LnNlYywKICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gYygiQUVkYXJrcmVkIiwgIkFFbGlnaHRncmVlbiIsICJBRWRhcmtncmVlbiIpLAogICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAic2FtcGxlX2NlbGx0eXBlIiwKICAgICAgICAgICAgICAgICAgIHN0YWNrID0gVFJVRSwKICAgICAgICAgICAgICAgICAgIGZsaXAgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgY29scyA9IGMoImRhcmtyZWQiLCAibGlnaHRncmVlbiIsICJkYXJrZ3JlZW4iKSxwdC5zaXplID0gMQogICAgICAgICAgICAgICAgICApICsKICAgIE5vTGVnZW5kKCkKCnZsbl9pbmQKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzJfQUVfc2VsZWN0ZWRfbW9kLnBkZiIsIGhlaWdodCA9IDIyLCB3aWR0aCA9IDIwKQp2bG5faW5kCmBgYAoKYGBge3J9ClZsblBsb3QoCiAgICBteS5zZWMsCiAgICBmZWF0dXJlcyA9IG1vZHNbbW9kdWxlX29yZGVyXSwKICAgIGdyb3VwLmJ5ID0gInNhbXBsZV9jZWxsdHlwZSIsCiAgICBzdGFjayA9IFRSVUUsIGZsaXAgPSBUUlVFLAogICAgY29scyA9IHN1YnN0cmluZyhtb2RzLCAzKVttb2R1bGVfb3JkZXJdKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9ICJkYXNoZWQiKQoKCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMl9BRV9ieV9jbHVzdGVyX2ludGVncmF0ZWRfZGF0YS5wZGYiLCBoZWlnaHQgPSAyMCwgd2lkdGggPSAzMCkKVmxuUGxvdCgKICAgIG15LnNlYywKICAgIGZlYXR1cmVzID0gbW9kc1ttb2R1bGVfb3JkZXJdLAogICAgZ3JvdXAuYnkgPSAic2FtcGxlX2NlbGx0eXBlIiwKICAgIHN0YWNrID0gVFJVRSwgZmxpcCA9IFRSVUUsCiAgICBjb2xzID0gc3Vic3RyaW5nKG1vZHMsIDMpW21vZHVsZV9vcmRlcl0pICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbHR5ID0gImRhc2hlZCIpCgoKYGBgCgpgYGB7cn0KIyBEYXRlIGFuZCB0aW1lIG9mIFJlbmRlcmluZwpTeXMudGltZSgpCgpzZXNzaW9uSW5mbygpCmBgYAoK